-- SyncResourceM
-- created by panyl
-- 同步资源模块

module("SyncResourceM", package.seeall);

local ENTIRE_PERIOD = 60;
local FAST_PERIOD   = 5;

local speed_up = 0;

local whenSendMessage;
local whenAttribUpdated;
local whenItemUpdated;
local whenMsgDone;

-- 目前仅关注两个属性
local attrib_map = {
    ["money"] = true,
    ["gen"]   = true,
};

local change_table = {};

local wait_attrib = {};

-- 同步状态
local sync_state = 0;

-- 模块初始化操作
function init()
    -- 如果是验证客户端，不处理了
    if isVerifyClient() then
        return;
    end

    change_table = {};

    sync_state = 0;

    EventMgr.removeAll("SyncResourceM");

    -- 发消息的事件
    EventMgr.register("SyncResourceM", event.SEND_MESSAGE, whenSendMessage);

    -- 属性事件
    EventMgr.register("SyncResourceM", event.BONUS_ATTRIB, whenAttribUpdated);
    EventMgr.register("SyncResourceM", event.COST_ATTRIB, whenAttribUpdated);

    -- 物品
    EventMgr.register("SyncResourceM", event.ITEM_COUNT_UPDATED, whenItemUpdated);

    -- 消息到达
    EventMgr.register("SyncResourceM", event.MESSAGE_DONE, whenMsgDone);

    -- 登录成功之后
    EventMgr.register("SyncResourceM", event.LOGIN_OK, function()
        -- 开始
        start_sync(true);
    end);
end

-- 消息变动
whenAttribUpdated = function(para)
    local attrib = para.attrib;

    if not attrib then
        return;
    end

    if not attrib_map[attrib] then
        return;
    end

    if -1 == sync_state then
        return;
    end

    local value = ME.user.dbase:query(attrib);
    if not value then
        return;
    end

    -- 属性有变动，记录下来
    change_table[attrib] = value;

    -- TODO: 属性有变动，同步无效
    sync_state = 0;
end

-- 物品数量变动
whenItemUpdated = function(classId)
    if -1 == sync_state then
        return;
    end

    -- TODO: 暂时仅关注合成材料（食材、武器）和天空飞艇物品
    local item_type = ItemM.query(classId, "type");
    if  ITEM_TYPE_MATERIAL ~= item_type         and
        ITEM_TYPE_FOOD     ~= item_type         and
        ITEM_TYPE_DEVICE_MATERIAL ~= item_type  and
        ITEM_TYPE_AIRSHIP_DEVICE  ~= item_type  then

        return;
    end

    change_table[classId] = ItemM.getAmount(ME.user, classId);

    -- TODO: 物品有变动，标记上次同步无效（如果还未返回同步结果）
    sync_state = 0;

    -- 如果是冈布奥碎片变化，需要加速一次
    if 17 == ItemM.query(classId, "group") then
        speed_up_sync();
    end
end

-- 发送消息时
whenSendMessage = function(para)
    local msg_no = para.msg_no;
    if not msg_no then
        return;
    end

    -- 如果是ping消息，不管了
    if MSG.getMsgName(msg_no) == "CMD_PING" then
        return;
    end

    if MSG.getMsgName(msg_no) == "CMD_SYNC_RESOURCE" then
        return;
    end

    -- TODO: 有消息变动，同步无效
    sync_state = 0;
end

-- 有消息到达时
whenMsgDone = function(para)
    -- 如果是ping消息，不管了
    if sizeof(para) < 2 then
        return;
    end

    if "MSG_PING" == para[1] then
        return;
    end

    if "MSG_SYNC_RESOURCE_RESULT" == para[1] then
        return;
    end

    -- TODO: 有消息到达，同步无效
    sync_state = 0;
end

-- 获取最近有变更的资源
function getVar(t)
    return change_table;
end

function start_sync(start)
    if start then
        -- 定时
        ScheduleM.createScheme("sync_resource", sync_resource, 1, true);
    else
        -- 删除定时器
        ScheduleM.deleteScheme("sync_resource");
    end
end

function sync_resource(force)
    -- 如果是在迷宫内
    if DungeonM.getState() == DUNGEON_GAMING then
        return;
    end

    -- 不在线了？
    if not ME.isInGame then
        return;
    end

    local last_time = ME.user.last_sync_resource or os.time();

    ME.user.last_sync_resource = last_time;

    -- 同步间隔
    local interval = ENTIRE_PERIOD;
    if speed_up > 0 then
        -- 加速了
        interval = FAST_PERIOD
    end

    if not force and last_time + interval > os.time() then
        return;
    end

    -- 速度降下来
    speed_up = 0;

    -- 如果是空的
    if sizeof(change_table) <= 0 then
        return;
    end

    -- 先更新下
    for _, attrib in pairs(table.keys(change_table)) do
        if type(attrib) == "string" then
            change_table[attrib] = ME.user.dbase:query(attrib);
        else
            change_table[attrib] = ItemM.getAmount(ME.user, attrib);
        end
    end

    -- 先标记同步
    sync_state = os.time();

    -- 收集需要同步的资源（不能超过100个，以免太大了）
    local t = change_table;

    if sizeof(change_table) >= 100 then
        t = {};

        local count = 0;
        for attrib, value in pairs(change_table) do
            t[attrib] = value;

            count = count + 1;
            if count >= 100 then
                break;
            end
        end
    end

    wait_attrib = t;

    ME.user.last_sync_resource = os.time();

    -- 同步
    Communicate.send("CMD_SYNC_RESOURCE", {["resource"] = t, ["cookie"] = sync_state, });
end

function sync_result(res, cookie)
    -- 如果被污染了
    if cookie ~= sync_state then
        trace("SyncResourceM", "cookie = %d, sync_state = %d", cookie, sync_state);

        -- 加速同步一次
        speed_up_sync();

        return;
    end

    -- 正在处理中，锁定
    sync_state = -1;

    cclog("同步结果：%o", res);

    local sync = function()
        wait_attrib = wait_attrib or res;

        for attrib, value in pairs(res) do
            if type(attrib) == "string" then
                if value >= 0 then
                    ME.user:addAttrib(attrib, value);
                else
                    ME.user:costAttrib(attrib, -value);
                end
            else
                if value >= 0 then
                    ItemM.addAmount(ME.user, attrib, value);
                else
                    ItemM.costAmount(ME.user, attrib, -value);
                end
            end
        end

        for attrib, _ in pairs(wait_attrib) do
            change_table[attrib] = nil;
        end
    end

    catch(sync);

    -- 重置状态
    sync_state = 0;
end

-- 加速同步
function speed_up_sync()
    -- 迷宫内不管了
    if DungeonM.getState() == DUNGEON_GAMING then
        return;
    end

    -- 状态不对
    if sync_state ~= 0 then
        return;
    end

    cclog("... 加速同步 ...");

    speed_up = speed_up + 1;
end
